package com.cnbm.wcsbasic.dispatch;

import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson.JSONObject;
import com.cnbm.wcsbasic.protocol.netty.s7.s7connector.enmuc.S7Client;
import com.cnbm.wcsbasic.protocol.netty.s7.s7connector.type.PlcVar;
import com.cnbm.wcsbasic.dispatch.constant.QGSConstant;
import com.cnbm.wcsbasic.dispatch.entity.DDJInfoCallBack;
import com.cnbm.wcsbasic.dispatch.entity.LineTaskDTO;
import com.cnbm.wcsbasic.dispatch.enums.line.plc1.Plc2WcsVar;
import com.cnbm.wcsbasic.dispatch.enums.line.plc1.Wcs2PlcVar;
import com.cnbm.wcsbasic.dispatch.interfaces.Listener;
import com.cnbm.wcsbasic.dispatch.interfaces.RuntimeEQErrListener;
import com.cnbm.wcsbasic.dispatch.interfaces.RuntimeLoggerListener;
import com.cnbm.wcsbasic.dispatch.taskPool.QGSThreadPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collection;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.BiConsumer;


/**
 * @Desc: ""
 * @Author: caixiang
 * @DATE: 2022/6/8 15:16
 */
public class DDJ1LineInTask implements Listener {
    //private static final Logger logger = LoggerFactory.getLogger(DDJ1Task.class);
    private static final Logger logger = LoggerFactory.getLogger("DDJ1LineIn");

    private String httpCallBackUrl = "";


    private LineTaskDTO taskDTO;
    private S7Client s7Client;

    public DDJ1LineInTask(LineTaskDTO taskDTO, S7Client s7Client){
        this.taskDTO = taskDTO;
        this.s7Client = s7Client;

    }


    public final Collection<RuntimeEQErrListener> runtimeErrRecvListeners = new CopyOnWriteArrayList<>();
    public final Collection<RuntimeLoggerListener> loggerRecvListeners = new CopyOnWriteArrayList<>();

    @Override
    public void addRuntimeEQErrListener(RuntimeEQErrListener lstnr) {
        runtimeErrRecvListeners.add(lstnr);
    }

    @Override
    public void addRuntimeLoggerListener(RuntimeLoggerListener strLog) {
        loggerRecvListeners.add(strLog);
    }

    public void executeRuntimeErrTask(Integer errCode) {
        for (RuntimeEQErrListener listener : runtimeErrRecvListeners) {
            listener.received(errCode);
        }
    }
    public void executeRuntimeLoggerTask(String strLog) {
        for (RuntimeLoggerListener listener : loggerRecvListeners) {
            listener.received(strLog);
        }
    }

    /* *
     *
     * status
     *      10   初始值
     *      -2   s7 数据采集失败
     *      100  任务成功完成
     *      101  任务被人为取消，取消的原因看 content ，tip:目前没有报错101 因为当设备故障后 我程序是阻塞住等成功标志位的。
     * */
    public void run(){
        CompletableFuture.supplyAsync(() -> {
            try {
                //调度逻辑 ------------------------------------- 开始 -------------------------------------

                //步骤1          WCS监听 PlcToWcs.Operate_Mode 是否为 1 , ( 1 = 联机 )
                Integer operateMode = waitingForTarget(Plc2WcsVar.EQ_STATUS_12001, 0);

                logger.info("");
                logger.info("=================  现在开始执行 DDJ1LineIn 任务 : "+ taskDTO.toString()+"  ==================");
                //业务记录一下任务开始

                //构建结果类
                DDJInfoCallBack build = new DDJInfoCallBack();
                build.setStatus(10);
                build.setTaskId(taskDTO.getTaskId());

                if(operateMode!=1){
                    logger.info("失败"+" --- "+"步骤1.  WCS监听 Plc2WcsVar.EQ_STATUS_12001 是否 等于 0 失败");
                    build.setContent("失败，步骤1.  WCS监听 Plc2WcsVar.EQ_STATUS_12001 是否 等于 0 失败");
                    build.setStatus(-2);
                    return build;
                }
                logger.info("成功"+" --- "+"步骤1.  WCS监听 Plc2WcsVar.EQ_STATUS_12001 是否 等于 0 ");


                //步骤2          Wcs2PlcVar.SCAN_CONFIRM_12001 置为 1 告诉 设备，开始称重
                write(s7Client, Wcs2PlcVar.SCAN_CONFIRM_12001,Byte.valueOf("1"));
                logger.info("成功"+" --- "+"步骤2.  Wcs2PlcVar.SCAN_CONFIRM_12001 置为 1");


                //步骤3
                //在此期间，如果Plc2WcsVar.BARCODE_READ_COMP_12002==false 设备会把货运出去，等待人工调整。
                Integer barcodeReadComp = waitingForTarget(Plc2WcsVar.BARCODE_READ_COMP_12002, true);
                if(barcodeReadComp!=1){
                    logger.info("失败"+" --- "+"步骤3.  WCS监听 Plc2WcsVar.BARCODE_READ_COMP_12002 是否 等于 true 失败");
                    build.setContent("失败，步骤3，WCS监听 Plc2WcsVar.BARCODE_READ_COMP_12002 是否 等于 true 失败");
                    build.setStatus(-2);
                    return build;
                }
                logger.info("成功"+" --- "+"步骤3.  WCS监听 Plc2WcsVar.BARCODE_READ_COMP_12002 等于 true ");


                //todo 暂时取消校验   -- 开始
                //步骤4
                //读条码值，然后比对
//            Integer barCodeRes = readBarcodeAndJudge(Plc2WcsVar.READED_CODE_12002, taskDTO.getBarCode());
//            if(barCodeRes==1){
//                //一致情况
//                //告诉plc ，码正确
//                write(s7Client, Wcs2PlcVar.SCAN_CONFIRM_12001,Byte.valueOf("2"));
//                logger.info("成功"+" --- "+"步骤4.  WCS 比对条码值 成功");
//            }else if(barCodeRes==2){
//                logger.info("失败"+" --- "+"步骤4.  WCS 比对条码值失败");
//                build.setContent("失败，步骤4.  WCS 比对条码值失败");
//                build.setStatus(-2);
//                return build;
//            }else if(barCodeRes==0){
//                //不一致 情况
//                //当条码不一致，设备会把码送回去，让现场人员调整，然后重新送回来。
//                write(s7Client, Wcs2PlcVar.SCAN_CONFIRM_12001,Byte.valueOf("3"));
//                try {
//                    //等待两秒，等待12002_BARCODE_READ_COMP 标志位为false
//                    Thread.sleep(2000);
//                }catch (Exception e){
//                    logger.info("失败"+" --- "+"Thread,sleep异常");
//                }
//                Integer barcodeReadCompInner = waitingForTarget(Plc2WcsVar.BARCODE_READ_COMP_12002, true);
//                if(barcodeReadCompInner!=1){
//                    logger.info("失败"+" --- "+"步骤4.  WCS 重新 监听 Plc2WcsVar.BARCODE_READ_COMP_12002 是否 等于 true 失败");
//                    build.setContent("失败，步骤4.  WCS 重新 监听 Plc2WcsVar.BARCODE_READ_COMP_12002 是否 等于 true 失败");
//                    build.setStatus(-2);
//                    return build;
//                }
//                logger.info("成功"+" --- "+"步骤4.  WCS 重新 监听 Plc2WcsVar.BARCODE_READ_COMP_12002 是否 等于 true");
//                Integer rebarCodeRes = readBarcodeAndJudge(Plc2WcsVar.READED_CODE_12002, taskDTO.getBarCode());
//                if(rebarCodeRes!=1){
//                    logger.info("失败"+" --- "+"步骤4.  WCS 重新 比对条码值，还是失败，结束任务");
//                    build.setContent("步骤4.  WCS 重新 比对条码值，还是失败，结束任务");
//                    build.setStatus(-2);
//                    return build;
//                }else if(rebarCodeRes==1){
//                    write(s7Client, Wcs2PlcVar.SCAN_CONFIRM_12001,Byte.valueOf("2"));
//                    logger.info("成功"+" --- "+"步骤4.  WCS 重新比对 比对条码值 成功");
//                }
//            }

                write(s7Client, Wcs2PlcVar.SCAN_CONFIRM_12001,Byte.valueOf("2"));
                //todo 暂时取消校验   -- 结束


                //步骤5
                Integer eqStatus = waitingForTarget(Plc2WcsVar.EQ_STATUS_12002, 0);
                if(eqStatus!=1){
                    logger.info("失败"+" --- "+"步骤5.  Plc2WcsVar.EQ_STATUS_12002 是否 等于 0 失败");
                    build.setContent("失败，步骤5.  Plc2WcsVar.EQ_STATUS_12002 是否 等于 0 失败");
                    build.setStatus(-2);
                    return build;
                }
                logger.info("成功"+" --- "+"步骤5.  Plc2WcsVar.EQ_STATUS_12002 等于 0");


                //步骤6          WCS下发任务
                issueTask();
                logger.info("成功"+" --- "+"步骤6.  WCS 下发任务 ");


                //步骤7          WCS启动任务（Wcs2PlcVar.RW_FLAG_12002 置为 1 ）
                write(s7Client, Wcs2PlcVar.RW_FLAG_12002,Byte.valueOf("1"));
                logger.info("成功"+" --- "+"步骤7.  WCS启动任务（Wcs2PlcVar.RW_FLAG_12002 置为 1 ）成功 ");


                //步骤8          WCS 监控Plc2WcsVar.EQ_STATUS_12003 == 2 监控任务是否完成
                Integer missionState2 = waitingForTargetAndLoggerError(Plc2WcsVar.EQ_STATUS_12003, 2);
                if(missionState2!=1){
                    logger.info("失败"+" --- "+"步骤8.  WCS 监控Plc2WcsVar.EQ_STATUS_12003 ==2 监控任务是否完成");
                    build.setContent("失败，步骤8.  WCS 监控Plc2WcsVar.EQ_STATUS_12003 ==2 失败");
                    build.setStatus(-2);
                    return build;
                }
                logger.info("成功"+" --- "+"步骤8.  WCS 监控Plc2WcsVar.EQ_STATUS_12003 ==2");


                //步骤9
//            if(!taskNoAndStockAndHeightIsOK()){
//                logger.info("失败"+" --- "+"步骤9.  WCS 监控任务号/isHeight/isStock 失败");
//            };
                while (true){
                    Boolean b = taskNoAndStockAndHeightIsOK();
                    if(b){
                        break;
                    }else {
                        try {
                            Thread.sleep(100000);
                        }catch (Exception e){

                        }
                    }
                }
                logger.info("成功"+" --- "+"步骤9.  WCS 监控任务号/isHeight/isStock 成功");


                //步骤10          WCS 把 Command_No 置为 2（重置操作）
                write(s7Client, Wcs2PlcVar.RW_FLAG_12003,Byte.valueOf("2"));
                logger.info("成功"+" --- "+"步骤10.  WCS 把 Wcs2PlcVar.RW_FLAG_12003 置为 2（重置操作） ");
                build.setStatus(100);
                build.setContent("任务成功完成");
                //调度逻辑 ------------------------------------- 结束

                return build;
            }catch (Throwable e){
                DDJInfoCallBack build = new DDJInfoCallBack();
                build.setTaskId(taskDTO.getTaskId());
                build.setContent("host:"+ s7Client.getHost()+e.getMessage());
                build.setStatus(-2);
                return build;
            }

        }, QGSThreadPool.ddj1LineInExecutorService).whenComplete(new BiConsumer<DDJInfoCallBack, Throwable>() {
            //任务完成以后把结果返回给 第调用者
            @Override
            public void accept(DDJInfoCallBack ddjInfoCallBack, Throwable throwable) {
                if(throwable!=null){
                    logger.info("whenComplete throwable is " + throwable.getMessage());
                }else {
                    logger.info("任务执行完成，现在wcs把 结果："+ddjInfoCallBack.toString()+", 推送给wms");

                    //todo 调试堆垛机，先把 这里给注释了
                    //HttpUtil.post(QGSConstant.lineInCallbackUrl, JSONObject.toJSONString(ddjInfoCallBack));
                    //链式构建请求
                    String result2 = HttpRequest.post(QGSConstant.lineInCallbackUrl)
                            .header("Content-Type", "application/json")
                            .header("tenant-id", "1")
                            .header("Authorization", "Bearer test1")
                            .body(JSONObject.toJSONString(ddjInfoCallBack))//表单内容
                            .timeout(20000)//超时，毫秒
                            .execute().body();
                    logger.info("=================  wms返回结果 : "+ result2+"  ==================");

                    logger.info("=================  任务结束 : "+ taskDTO.toString()+"  ==================");
                    logger.info("");
                }
            }
        });

    }


    /**
     * desc: 下发任务给电控
     * return：
     *      true  成功
     *      false 失败
     * */
    private void issueTask() {
        write(s7Client,Wcs2PlcVar.MISSION_TYPE_12002, Byte.valueOf(taskDTO.getMissionType().toString()));

        write(s7Client,Wcs2PlcVar.TASK_ID_12002, taskDTO.getTaskId());
        write(s7Client,Wcs2PlcVar.DEST_EQ_ID_12002, Integer.valueOf(12003));
        write(s7Client,Wcs2PlcVar.EQ_UNLOCK_12002, Byte.valueOf("1"));
        write(s7Client,Wcs2PlcVar.GOODS_SIZE_12002, Byte.valueOf("0"));
        write(s7Client,Wcs2PlcVar.ACTION_TYPE_12002, Byte.valueOf("1"));

    }

    /**
     * desc: 判断任务号stock height是否正确
     * return：
     *      true  成功
     *      false 失败
     * */
    private Boolean taskNoAndStockAndHeightIsOK() {
        Integer taskNo = (Integer) read(s7Client,Plc2WcsVar.TASK_NO_12003);
        Boolean isStock = (Boolean) read(s7Client,Plc2WcsVar.IS_STOCK_12003);
        Boolean isHeight = (Boolean) read(s7Client,Plc2WcsVar.IS_HEIGHT_12003);
        if(taskNo.equals(taskDTO.getTaskId()) && isStock.equals(true) && isHeight.equals(true)){
            return true;
        }else {
            logger.info("步骤9.  WCS 监控任务号/isHeight/isStock 失败, taskNo: "+taskNo+", isStock: "+isStock+", isHeight: "+isHeight);
            return false;
        }
    }


    /**
     * arg:
     *      RGV1Plc2WcsVarEnum 参数类型
     *      targetValue        目的值
     * return:
     *      2 opc 访问异常;
     *      1 成功;
     **/
    private Integer waitingForTarget(Plc2WcsVar var, Integer targetValue) {
        Integer res = 0;
        while (true){
            try {
                Integer now = (Integer) read(s7Client,var);
                if(now == null){
                    Thread.sleep(2000);
                    throw new Exception("");
                }
                if(targetValue.equals(now)){
                    res = 1;
                    break;
                }
                Thread.sleep(200);
            }catch (Exception e){
                logger.info("S7 数据采集 异常. 监听 变量 ："+var.getName()+" 为目标值："+targetValue+"失败");
                res = 2;
                break;
            }
        }
        return res;
    }
    private Integer waitingForTarget(Plc2WcsVar var, Boolean targetValue) {
        Integer res = 0;
        while (true){
            try {
                Boolean now = (Boolean) read(s7Client,var);
                if(now == null){
                    Thread.sleep(2000);
                    throw new Exception("");
                }
                if(targetValue == now){
                    res = 1;
                    break;
                }
                Thread.sleep(200);
            }catch (Exception e){
                logger.info("S7 数据采集 异常. 监听 变量 ："+var.getName()+" 为目标值："+targetValue+"失败");
                res = 2;
                break;
            }
        }
        return res;
    }

    /**
     * desc: 和目标值匹配
     *  一致：     返回1
     *  不一致：   返回0
     *  异常：     返回2
     * */
    private Integer readBarcodeAndJudge(Plc2WcsVar var, String targetValue) {
        try {
            String now = (String) read(s7Client,var);
            if(now == null){
                throw new Exception("");
            }
            if(targetValue.equals(now)){
                return 1;
            }
            logger.info("比对条码值 失败. 监听 变量 ："+var.getName()+" 为读取值："+now+", 目标值："+targetValue);
            return 0;
        }catch (Exception e){
            logger.info("S7 数据采集 异常. 监听 变量 ："+var.getName()+" 为目标值："+targetValue+"失败");
            return 2;
        }
    }


    private Integer waitingForTargetAndLoggerError(Plc2WcsVar var,Integer targetValue) {
        Integer res = 0;
        while (true){
            try {
                Integer now = (Integer) read(s7Client,var);

                if(now == null){
                    Thread.sleep(2000);
                    throw new Exception("");
                }
                if(targetValue.equals(now)){
                    res = 1;
                    break;
                }
                //当在运行的过程中设备出现告警了。
                if(now>2){
                    Integer faultNo = (Integer) read(s7Client,var);
                    logger.info("步骤8  传输线在运行的时候出现 fault, fault no = "+faultNo);
                    //调用上层的 回调方法     开始
                    executeRuntimeErrTask(faultNo);
                    //调用上层的 回调方法     结束
                    Thread.sleep(10000);
                }
                Thread.sleep(200);
            }catch (Exception e){
                logger.info("S7 数据采集 异常. 监听 变量 ："+var.getName()+" 为目标值："+targetValue+"失败");
                res = 2;
                break;
            }
        }
        return res;
    }


    /**
     * return
     *          成功： 返回相应的object对象
     *          失败： 返回null
     * */
    private Object read(S7Client s7Client, Plc2WcsVar var) {
        try {
            return s7Client.read(var.getArea(), var.getAreaNumber(), var.getByteOffset(), var.getBitOffset(), var.getLength(), var.getStrSize(), var.getType());
        }catch (Exception e){
            logger.info("host:"+s7Client.getHost()+" ; read 操作出现问题: {}",e.getMessage(),e);
            return null;
        }
    }

    private void write(S7Client s7Client, Wcs2PlcVar var, Object newValue) {
        if(var.getType().equals(PlcVar.STRING_Array)){
            String[] s = (String[])newValue;
            String[] ss = (String[])newValue;
            if(s.length > var.getLength() ){
                ss = new String[var.getLength()];
                for(int i=0;i< var.getLength();i++){
                    ss[i] = s[i];
                }
            }
            s7Client.write(var.getArea(), var.getAreaNumber(), var.getByteOffset(), var.getBitOffset(), var.getStrSize(), var.getType(),ss);
        }else {
            s7Client.write(var.getArea(), var.getAreaNumber(), var.getByteOffset(), var.getBitOffset(), var.getStrSize(), var.getType(),newValue);
        }
    }


}
